package com.henghe.frame.core.autoConfiguration.encry;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.henghe.frame.common.entity.InputStreamHttpServletRequestWrapper;
import com.henghe.frame.common.entity.Result;
import com.henghe.frame.common.util.AESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Description:接口加解密aop
 * Author: LiuWenQing
 * Datetime: 2023/3/28 09:42
 */
@Aspect
@Component
@Slf4j
public class EncryAop {

    @Value("${encry.exclude-path}")
    private String excludePath;

    @Value("${encry.aes-key}")
    private String aesKey;

    private static List<Pattern> patterns = new ArrayList();

    @PostConstruct
    public void init() {
        if (StringUtils.isNotEmpty(excludePath)) {
            String[] filters = this.excludePath.split(",");
            String[] var3 = filters;
            int var4 = filters.length;

            for (int var5 = 0; var5 < var4; ++var5) {
                String filter = var3[var5];
                patterns.add(Pattern.compile(filter));
            }
        }
        patterns.add(Pattern.compile("v2/api-docs.*"));
        patterns.add(Pattern.compile("configuration/ui.*"));
        patterns.add(Pattern.compile("swagger-resources.*"));
        patterns.add(Pattern.compile("configuration/security.*"));
        patterns.add(Pattern.compile("swagger-ui.html"));
        patterns.add(Pattern.compile("spec"));
        patterns.add(Pattern.compile("healthy"));
        patterns.add(Pattern.compile("webjars.*"));
        patterns.add(Pattern.compile("error"));
        patterns.add(Pattern.compile("druid/*.*"));
        patterns.add(Pattern.compile("doc.html"));
    }


    @Pointcut("(@annotation(org.springframework.web.bind.annotation.RequestMapping)) " +
            "|| (@annotation(org.springframework.web.bind.annotation.GetMapping)) " +
            "|| (@annotation(org.springframework.web.bind.annotation.PostMapping)) " +
            "|| (@annotation(org.springframework.web.bind.annotation.PutMapping)) " +
            "|| (@annotation(org.springframework.web.bind.annotation.PatchMapping)) " +
            "|| (@annotation(org.springframework.web.bind.annotation.DeleteMapping)) ")
    public void methodPointCut() {
    }

    @Around("methodPointCut()")
    public Object runTimeStatistics(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String url = request.getRequestURI().substring(request.getContextPath().length());
        if (url.startsWith("/") && url.length() > 1) {
            url = url.substring(1);
        }
        /**
         * 忽略的路径直接过滤掉
         */
        if (isInclude(url)) {
            return pjp.proceed();
        }

        /**
         * GET、DELETE请求取Query参数进行解密
         * POST、PUT、PATCH请求取Body参数进行解密
         */
        if (request.getMethod().equals("GET") || request.getMethod().equals("DELETE")) {
            Object[] requestParams = pjp.getArgs();
            Object[] encryedParams = new Object[requestParams.length];
            Integer index = 0;
            // 参数加密
            for (Object requestParam : requestParams) {
                encryedParams[index] = Objects.nonNull(requestParam) ?
                        AESUtil.decrypt(requestParam.toString(), aesKey) : "";
                index++;
            }
            Object response = pjp.proceed(encryedParams);
            return handleResponse(response);
        } else {
            // 非get delete请求直接拿body参数进行解密
            InputStreamHttpServletRequestWrapper inputStreamHttpServletRequestWrapper = new InputStreamHttpServletRequestWrapper(request);
            String bodyMessage = inputStreamHttpServletRequestWrapper.body;
            if (StringUtils.isNotEmpty(bodyMessage)) {
                String decryStr = "";
                try {
                    JSONObject jsonObject = JSON.parseObject(bodyMessage);
                    decryStr = AESUtil.decrypt(jsonObject.getString("encryData"), aesKey);
                } catch (Exception exception) {
                    exception.printStackTrace();
                    return pjp.proceed();
                }
                if (StringUtils.isNotEmpty(decryStr)) {
                    JSONObject jsonObject = JSON.parseObject(decryStr);
                    Set<String> keys = jsonObject.keySet();
                    Object[] args = pjp.getArgs();
                    for (Object arg : args) {
                        Class c = arg.getClass();
                        Field[] declaredFields = c.getDeclaredFields();
                        for (Field declaredField : declaredFields) {
                            declaredField.setAccessible(true);
                            if (keys.contains(declaredField.getName())) {
                                Object o = jsonObject.get(declaredField.getName());
                                if(o instanceof Integer && declaredField.getType().equals(Long.class)){
                                    declaredField.set(arg,Long.parseLong(o.toString()));
                                }else if(o instanceof Long && declaredField.getType().equals(Integer.class)){
                                    declaredField.set(arg,Integer.parseInt(o.toString()));
                                }else{
                                    declaredField.set(arg, o);
                                }
                            }
                        }
                    }
                    return handleResponse(pjp.proceed(args));
                }
            }
            return handleResponse(pjp.proceed());
        }
    }

    private Object handleResponse(Object response){
        if (response instanceof Result) {
            Result r = (Result) response;
            if (Objects.nonNull(r.getData()) && StringUtils.isNotEmpty(r.getData().toString())) {
                r.setData(AESUtil.encrypt(r.getData().toString(), aesKey));
            }
            return r;
        }
        return response;
    }

    private boolean isInclude(String url) {
        Iterator var2 = patterns.iterator();
        Matcher matcher;
        do {
            if (!var2.hasNext()) {
                return false;
            }
            Pattern pattern = (Pattern) var2.next();
            matcher = pattern.matcher(url);
        } while (!matcher.matches());
        return true;
    }
}
